#include "pch.h"
#include "DesktopFrame.h"
#include "SharedMemory.h"

#include <utility>

DesktopFrame::DesktopFrame(DesktopSize size,
	int stride,
	uint8_t* data,
	SharedMemory* shared_memory)
	: data_(data),
	shared_memory_(shared_memory),
	size_(size),
	stride_(stride),
	capture_time_ms_(0),
	capturer_id_(0) {
}

DesktopFrame::~DesktopFrame() = default;

void DesktopFrame::CopyPixelsFrom(const uint8_t* src_buffer,
	int src_stride,
	const DesktopRect& dest_rect) {

	uint8_t* dest = GetFrameDataAtPos(dest_rect.top_left());
	for (int y = 0; y < dest_rect.height(); ++y) {
		memcpy(dest, src_buffer, DesktopFrame::kBytesPerPixel * dest_rect.width());
		src_buffer += src_stride;
		dest += stride();
	}
}

void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
	const DesktopVector& src_pos,
	const DesktopRect& dest_rect) {

	CopyPixelsFrom(src_frame.GetFrameDataAtPos(src_pos), src_frame.stride(),
		dest_rect);
}

DesktopRect DesktopFrame::rect() const {
	const float scale = scale_factor();
	return DesktopRect::MakeXYWH(top_left().x(), top_left().y(),
		size().width() / scale, size().height() / scale);
}

float DesktopFrame::scale_factor() const {
	float scale = 0.5f;

	return scale;
}

uint8_t* DesktopFrame::GetFrameDataAtPos(const DesktopVector& pos) const {
	return data() + stride() * pos.y() + DesktopFrame::kBytesPerPixel * pos.x();
}

void DesktopFrame::CopyFrameInfoFrom(const DesktopFrame& other) {
	set_dpi(other.dpi());
	set_capture_time_ms(other.capture_time_ms());
	set_capturer_id(other.capturer_id());
	*mutable_updated_region() = other.updated_region();
	set_top_left(other.top_left());
}

void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) {
	set_dpi(other->dpi());
	set_capture_time_ms(other->capture_time_ms());
	set_capturer_id(other->capturer_id());
	mutable_updated_region()->Swap(other->mutable_updated_region());
	set_top_left(other->top_left());
}

BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
	: DesktopFrame(size,
		kBytesPerPixel * size.width(),
		new uint8_t[kBytesPerPixel * size.width() * size.height()](),
		nullptr) {}

BasicDesktopFrame::~BasicDesktopFrame() {
	delete[] data_;
}

DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) {
	DesktopFrame* result = new BasicDesktopFrame(frame.size());
	for (int y = 0; y < frame.size().height(); ++y) {
		memcpy(result->data() + y * result->stride(),
			frame.data() + y * frame.stride(),
			frame.size().width() * kBytesPerPixel);
	}
	result->CopyFrameInfoFrom(frame);
	return result;
}

std::unique_ptr<DesktopFrame> SharedMemoryDesktopFrame::Create(
	DesktopSize size,
	SharedMemoryFactory* shared_memory_factory) {

	size_t buffer_size = size.height() * size.width() * kBytesPerPixel;
	std::unique_ptr<SharedMemory> shared_memory =
		shared_memory_factory->CreateSharedMemory(buffer_size);
	if (!shared_memory)
		return nullptr;

	return std::make_unique<SharedMemoryDesktopFrame>(
		size, size.width() * kBytesPerPixel, std::move(shared_memory));
}

SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(DesktopSize size,
	int stride,
	SharedMemory* shared_memory)
	: DesktopFrame(size,
		stride,
		reinterpret_cast<uint8_t*>(shared_memory->data()),
		shared_memory) {}

SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(
	DesktopSize size,
	int stride,
	std::unique_ptr<SharedMemory> shared_memory)
	: SharedMemoryDesktopFrame(size, stride, shared_memory.release()) {}

SharedMemoryDesktopFrame::~SharedMemoryDesktopFrame() {
	delete shared_memory_;
}
